//
//  RealtimeTranslatorController.swift
//  GLUZSwift
//
//  Created by xyanl on 2025/4/11.
//
//  同声传译
import UIKit
import SnapKit
import Reusable
import JKSwiftExtension
import ZegoExpressEngine
import JFPopup

enum RealtimeTranslatorState {
    // 未选中
    case none
    // 俄语
    case ru
    // 中文
    case zh
}

class RealtimeTranslatorController: BaseViewController {
    
    private lazy var barView: RealtimeTranslatorBarView = {
        let barView = RealtimeTranslatorBarView.loadFromNib()
        barView.didSelectedItemBlock = { [weak self] state in
            self?.state = state
        }
        return barView
    }()
    
    private lazy var changeButton: UIButton = {
        let button = UIButton(type: .custom)
        button.frame = CGRect(x: 0, y: 0, width: 44, height: 44)
        button.setImage("ic_rm_change".toImage, for: .normal)
        button.addTarget(self, action: #selector(changeOrientationAction), for: .touchUpInside)
        return button
    }()
    
    private var ruView = UIView()
    private var zhView = UIView()
    private var waveView = AUWaveView()
    
    private lazy var ruPlaceholderView: RealtimeTranslatorPlaceholderCell = {
        let view = RealtimeTranslatorPlaceholderCell.loadFromNib()
        view.titleLabel.text = "Нажмите, скажите на русском."
        return view
    }()
    
    private lazy var zhPlaceholderView: RealtimeTranslatorPlaceholderCell = {
        let view = RealtimeTranslatorPlaceholderCell.loadFromNib()
        view.titleLabel.text = "点击一下说中文"
        return view
    }()
    
    /// 俄语
    private lazy var ruTableView: UITableView = {
        createTableView(style: .plain)
    }()
    
    /// 中文
    private lazy var zhTableView: UITableView = {
        createTableView(style: .plain)
    }()
    
    private var currentTime: Int = 0
    
    //    private lazy var speakerButton: UIButton = {
    //        let button = UIButton(type: .custom)
    //        button.isHidden = true
    //        button.setImage("ic_rm_speaker_open".toImage, for: .normal)
    //        button.setImage("ic_rm_speaker_close".toImage, for: .selected)
    //        button.addTarget(self, action: #selector(speakerAction), for: .touchUpInside)
    //        return button
    //    }()
    
    var ruList: [NeoNuiResultModel] = []
    var zhList: [NeoNuiResultModel] = []
    
    var state: RealtimeTranslatorState = .none {
        didSet {
            
            if oldValue == state, state != .none {
                xLog("重复设置")
                return
            }
            
            switch state {
            case .none:
                ruList = []
                zhList = []
                
            case .ru:
                ruView.backgroundColor = .clear
                zhView.backgroundColor = .white
                ruPlaceholderView.isHidden = true
                zhPlaceholderView.isHidden = true
                YLHUD.showMessage("Говорит по - русски.", toView: ruTableView)
                
                // 切换语音
                startNeoNui(.ru)
                
            case .zh:
                ruView.backgroundColor = .white
                zhView.backgroundColor = .clear
                ruPlaceholderView.isHidden = true
                zhPlaceholderView.isHidden = true
                YLHUD.showMessage("正在说中文", toView: zhTableView)
                
                startNeoNui(.zh)
                
            }
            
            if barView.state != state {
                barView.state = state
            }
            
            ruTableView.reloadData()
            zhTableView.reloadData()
        }
    }
    
    private lazy var ruNeoNui = {
        let neoNui = NeoNui()
        neoNui.delegate = self
        return neoNui
    }()
    
    private lazy var zhNeoNui = {
        let neoNui = NeoNui()
        neoNui.delegate = self
        return neoNui
    }()
    
    var languageType: NeoNuiType = .zh
    
    private var emptyCount = 0
    /// 麦克风收录的音频数据
    private var recordedVoiceData = Data()
    /// 同步锁对象
    private let recordedVoiceDataLock = NSObject()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initNav()
        initUI()
        initNeoNui()
        startAudioDataObserver()
        showPopupView()
    }
    
    open override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        UIApplication.sg.isIdleTimerDisabled(true)
    }
    
    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        UIApplication.sg.isIdleTimerDisabled(false)
        stopAudioDataObserver()
    }
    
    deinit {
        xLog("deinit -- \(className)")
    }
}

private extension RealtimeTranslatorController {
    
    func initNav() {
        vhl_navBarBackgroundAlpha = 0
        navigationItem.rightBarButtonItem = UIBarButtonItem(customView: changeButton)
    }
    
    func initUI() {
        view.backgroundColor = .gradient(colors: [
            UIColor(hex: "#CAF2FE"),
            UIColor(hex: "#B6EEFF"),
            UIColor(hex: "#CBF1FE"),
        ], rect: UIScreen.main.bounds)
        view.addSubview(barView)
        
        barView.snp.makeConstraints { make in
            make.left.equalToSuperview().offset(16)
            make.right.equalToSuperview().offset(-16)
            make.bottom.equalToSuperview().offset(-xIphoneBottomSafeHeight)
            make.height.equalTo(44)
        }
        
        /// 声波高度
        let waveHeight: CGFloat = 64
        
        ruView.backgroundColor = .white
        view.addSubview(ruView)
        ruView.snp.makeConstraints { make in
            make.top.left.right.equalToSuperview()
            make.height.equalTo(xScreenH*0.5)
        }
        
        ruView.addSubview(ruPlaceholderView)
        ruPlaceholderView.snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.top.equalToSuperview().offset(xIphoneNavStatusBarHeight)
            make.bottom.equalToSuperview().offset(-waveHeight*0.5)
        }
        ruTableView.backgroundColor = .clear
        ruTableView.tag = 0
        ruView.addSubview(ruTableView)
        ruTableView.snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.top.equalToSuperview().offset(xIphoneNavStatusBarHeight)
            make.bottom.equalToSuperview().offset(-waveHeight*0.5)
        }
        
        zhView.backgroundColor = .white
        view.addSubview(zhView)
        zhView.snp.makeConstraints { make in
            make.left.bottom.right.equalToSuperview()
            make.height.equalTo(xScreenH*0.5)
        }
        zhView.addSubview(zhPlaceholderView)
        zhPlaceholderView.snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.top.equalToSuperview().offset(waveHeight*0.5)
            make.bottom.equalTo(barView.snp.top)
        }
        zhTableView.backgroundColor = .clear
        zhTableView.tag = 1
        zhView.addSubview(zhTableView)
        zhTableView.snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.top.equalToSuperview().offset(waveHeight*0.5)
            make.bottom.equalTo(barView.snp.top)
        }
        
        view.bringSubviewToFront(barView)
        state = .none
        
        view.addSubview(waveView)
        waveView.snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.centerY.equalToSuperview()
            make.height.equalTo(64)
        }
        waveView.amplitude = 0.3
        
    }
    
    
    /// 创建 UITableView
    /// - Parameter style: style description
    func createTableView(style: UITableView.Style) -> UITableView {
        let tableView = UITableView(frame: .zero, style: style)
        
        if style != .insetGrouped {
            tableView.backgroundColor = .white
        }
        
        // .never: 第一个cell挨着屏幕顶, 否则是挨着navbar底部
        tableView.contentInsetAdjustmentBehavior = .never
        // 去掉分割线
        tableView.separatorStyle = .none
        // 修复默认分割线, 向右偏移问题
        // tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        // 分割线颜色
        // tableView.separatorColor = .red
        // iOS 15 默认情况 sectionHeaderTopPadding 会有22个像素的高度，及默认情况，TableView section header 增加22像素的高度
        if #available(iOS 15.0, *) {
            tableView.sectionHeaderTopPadding = 0
        }
        // 解决 textView 长按 放大镜滑动时,tableView也跟着滑动
        tableView.canCancelContentTouches = false
        tableView.delaysContentTouches = true
        
        // 隐藏滚动条
        tableView.showsVerticalScrollIndicator = false
        tableView.showsHorizontalScrollIndicator = false
        
        tableView.estimatedSectionHeaderHeight = 0
        tableView.estimatedSectionFooterHeight = 0
        
        // 滚动时隐藏键盘
        tableView.keyboardDismissMode = .onDrag
        
        // 去掉多余的 cell
        tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: xScreenW, height: CGFLOAT_MIN))
        tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: xScreenW, height: CGFLOAT_MIN))
        
        tableView.delegate = self
        tableView.dataSource = self
        
        tableView.register(cellType: RealtimeTranslatorCell.self)
        tableView.sg.addTapGesture(self, #selector(changeTableViewAction))
        
        return tableView
    }
    
    @objc func switchLanguageAction(sender: UIButton) {
        state = sender.tag == 0 ? .zh: .ru
    }
    
    
    func showPopupView() {
        
        YLRequest.request(LiveAPI.systemMsg) { result in
            switch result {
            case .success(let res):
                
                let dataDictionary = res.dataDictionary
                if let model = JsonUtil.modelFromDictionary(dataDictionary, RealtimeTranslationPopupConfig.self) {
                    RealtimeTranslationPopupManager.shared.save(model)
                    
                    if RealtimeTranslationPopupManager.shared.isMoreTime() {
                        RealtimeTranslationPopupManager.shared.printData()
                        // 还有可以显示的次数,显示
                        DispatchQueue.main.asyncAfter(wallDeadline: .now() + 0.5) { [weak self] in
                            let alerView = RealtimeTranslatorAlertView.loadFromNib()
                            alerView.closeBlock = { [weak self] in
                                self?.popup.dismissPopup()
                            }
                            self?.popup.dialog {
                                return alerView
                            }
                            
                        }
                    }
                }
                
            case .failure(let err):
                YLHUD.showHint(.error, err.errMsg)
            }
        }
        
    }
    
}

private extension RealtimeTranslatorController {
    // 修改对方的显示方向
    @objc func changeOrientationAction(sender: UIButton) {
        sender.layer.jk.animationRotation(rotation: sender.isSelected ? Double.pi*2: Double.pi, duration: 0.5, delay: 0, repeatNumber: 1, removedOnCompletion: false, option: .default)
        ruTableView.layer.jk.animationRotation(rotation: sender.isSelected ? Double.pi*2: Double.pi, duration: 0.5, delay: 0, repeatNumber: 1, removedOnCompletion: false, option: .default)
        sender.isSelected = !sender.isSelected
        
    }
    
    @objc func speakerAction(sender: UIButton) {
        sender.isSelected = !sender.isSelected
        
    }
    
    @objc func changeTableViewAction(tap: UITapGestureRecognizer) {
        guard let tag = tap.view?.tag else {
            return
        }
        
        if tag == 0 {
            state = .ru
        } else {
            state = .zh
        }
        
    }
    
    /// 滚动到最下
    func scrollToBottom(tableView: UITableView, list: [Any]) {
        
        UIView.animate(withDuration: 0, delay: 1.0, options: .allowUserInteraction, animations: {}) { _ in
            if tableView.isTracking || tableView.isDragging || tableView.isDecelerating {
                return
            }
            
            let index = list.count - 1
            if index >= 0 {
                tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .bottom, animated: true)
            }
        }
    }
    
    
}
extension RealtimeTranslatorController: UITableViewDelegate, UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let list = tableView.tag == 0 ? ruList: zhList
        return list.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let list = tableView.tag == 0 ? ruList: zhList
        let cell = tableView.dequeueReusableCell(for: indexPath, cellType: RealtimeTranslatorCell.self)
        let model: NeoNuiResultModel = list[indexPath.row]
        cell.contentLabel.text = tableView.tag == 0 ? model.ruText: model.zhText
        cell.contentLabel.textColor = UIColor(hex: "#333333")
        if indexPath.row == list.count - 1 {
            cell.contentLabel.font = .systemFont(ofSize: 15, weight: .medium)
        } else {
            cell.contentLabel.font = .systemFont(ofSize: 15, weight: .regular)
            cell.contentLabel.textColor = UIColor(hex: "#333333")
        }
        var subtitleColor = UIColor(hex: "#333333")
        // 旧的字体大小
        var font = UIFont.systemFont(ofSize: 15, weight: .regular)
        if indexPath.row == list.count - 1 {
            // 最新显示的字体大小
            font = UIFont.systemFont(ofSize: 18, weight: .medium)
            
        } else {
            if tableView.tag == 0, state == .ru {
                subtitleColor = UIColor(hex: "#006888")
            }
            
            if tableView.tag == 1, state == .zh {
                subtitleColor = UIColor(hex: "#006888")
            }
        }
        
        cell.contentLabel.font = font
        cell.contentLabel.textColor = subtitleColor
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let list = tableView.tag == 0 ? ruList: zhList
        let model: NeoNuiResultModel = list[indexPath.row]
        let text = tableView.tag == 0 ? model.ruText: model.zhText
        
        var font = UIFont.systemFont(ofSize: 15, weight: .regular)
        if indexPath.row == list.count - 1 {
            font = UIFont.systemFont(ofSize: 18, weight: .medium)
        }
        
        return text.sg.size(width: xScreenW - 16*2 - 50, font: font).height + 16
    }
    
}

// MARK: - 翻译
extension RealtimeTranslatorController {
    
    func initNeoNui() {
        xLog("[NeoNui] 初始化")
        // 请注意此处的参数配置，其中账号相关需要按照genInitParams的说明填入后才可访问服务
        ruNeoNui.nui_initialize(genParams(.ru), logLevel: NUI_LOG_LEVEL_NONE, saveLog: false)
        ruNeoNui.nui_set_params(configParams(.ru))
        
        zhNeoNui.nui_initialize(genParams(.zh), logLevel: NUI_LOG_LEVEL_NONE, saveLog: false)
        zhNeoNui.nui_set_params(configParams(.zh))
        
    }
    
    
    /// 开始小牛
    func startNeoNui(_ language: NeoNuiType) {
        currentTime = 0
        if language == .ru {
            zhNeoNui.nui_dialog_cancel(true)
            xLog("[NeoNui] 启动俄语")
            DispatchQueue.global().asyncAfter(wallDeadline: .now() + 0) { [weak self] in
                self?.languageType = .ru
                self?.recordedVoiceData = Data()
                self?.ruNeoNui.nui_dialog_start(MODE_P2T, dialogParam: "".cString(using: .utf8))
            }
            
        } else {
            ruNeoNui.nui_dialog_cancel(true)
            xLog("[NeoNui] 启动中文")
            DispatchQueue.global().asyncAfter(wallDeadline: .now() + 0) { [weak self] in
                self?.languageType = .zh
                self?.recordedVoiceData = Data()
                self?.zhNeoNui.nui_dialog_start(MODE_P2T, dialogParam: "".cString(using: .utf8))
            }
        }
    }
    
    private func genParams(_ language: NeoNuiType) -> [CChar]? {
        // let token = UserProfile.token + (language == .zh ? "zh": "ru")
        var params: [String: Any] = [
            "app_key": "default",
            // 获取临时 token https://nls-portal.console.aliyun.com/overview
            // @"token": @"8eee6741ef574a31928913b745297978",
            "token": "default",
            //当初始化SDK时的save_log参数取值为true时，该参数生效。表示是否保存音频debug，该数据保存在debug目录中，需要确保debug_path有效可写
            "save_wav": "false",
            "service_mode": "1",
        ]
        
        if language == .zh {
            params["url"] = "wss://1an04la087286.vicp.fun/wsttrans/ws/zh?apikey=3bc9127c78fe827bb726108bb3559ba4"
        } else {
            params["url"] = "wss://1an04la087286.vicp.fun/wsttrans/ws/ru?apikey=3bc9127c78fe827bb726108bb3559ba4"
        }
        
        xLog("[NeoNui] genParams \((language == .zh ? "zh": "ru")): \(params as NSDictionary)")
        
        // 方式 2：直接获取可选项（注意：可能返回 nil）
        if let cString = params.toJSONString()?.cString(using: .utf8) {
            // cString 类型是 [CChar]，且已自动添加 NULL 终止符
            return cString
        }
        
        return nil
    }
    
    private func configParams(_ language: NeoNuiType) -> [CChar]? {
        // 参数可根据实际业务进行配置
        // 接口说明可见https://help.aliyun.com/document_detail/173528.html
        let config: [String: Any] = [
            // 是否返回中间识别结果
            "enable_intermediate_result": true,
            // 是否在后处理中添加标点
            "enable_punctuation_prediction": true,
            // ITN（逆文本inverse text normalization）中文数字转换阿拉伯数字。设置为True时，中文数字将转为阿拉伯数字输出，默认值：False。
            // 中文的时候 true,俄语的时候 false 否则会造成俄语没有空格断单词
            "enable_inverse_text_normalization": language == .zh,
            // 是否开启语义断句，可选，默认是False。语义断句参数需要和开启中间结果配合使用，即开启该语义断句参数需将中间结果参数同时打开：enable_intermediate_result=true。
            // "enable_semantic_sentence_detection": true,
            "sample_rate": 16000,
            "sr_format": "pcm",
        ]
        
        let params: [String: Any] = [
            "nls_config": config,
            "service_type": 4
        ]
        
        // 直接获取可选项（注意：可能返回 nil）
        if let cString = params.toJSONString()?.cString(using: .utf8) {
            // cString 类型是 [CChar]，且已自动添加 NULL 终止符
            return cString
        }
        
        return nil
    }
    
    func neoNuiRealtimeTranslation(_ text: String, languageType: NeoNuiType, isEnd: Bool) {
        
        if text.isEmpty { return }
        
        let from = languageType == .ru ? "ru": "zh"
        let to = languageType == .ru ? "zh": "ru"
        
        if !isEnd {
            // 中间过程
            if currentTime == 0 {
                // 还未显示内容,
                let realtimeModel = NeoNuiResultModel(language: languageType, zhText: "", ruText: "")
                currentTime = realtimeModel.id
                
                if languageType == .ru {
                    realtimeModel.ruText = text
                    self.ruList.append(realtimeModel)
                    self.ruTableView.reloadData()
                    self.scrollToBottom(tableView: self.ruTableView, list: self.ruList)
                    
                } else {
                    realtimeModel.zhText = text
                    self.zhList.append(realtimeModel)
                    self.zhTableView.reloadData()
                    self.scrollToBottom(tableView: self.zhTableView, list: self.zhList)
                }
                
            } else {
                // 已经显示过了, 需要更新
                if languageType == .ru {
                    ruList.forEach { model in
                        if model.id == currentTime {
                            model.ruText = text
                        }
                    }
                    self.ruTableView.reloadData()
                    self.scrollToBottom(tableView: self.ruTableView, list: self.ruList)
                    
                } else {
                    zhList.forEach { model in
                        if model.id == currentTime {
                            model.zhText = text
                        }
                    }
                    self.zhTableView.reloadData()
                    self.scrollToBottom(tableView: self.zhTableView, list: self.zhList)
                }
            }
            
            return
        }
        
        
        NeoNuiTranslation.translations(id: currentTime, from: from, to: to, text: text) { id, results  in
            DispatchQueue.main.async { [weak self] in
                
                guard let self = self else { return }
                
                // 原文
                let sourceText = results.map { $0.sourceText }.joined()
                // 翻译结果
                let transText = results.map { $0.transText }.joined()
                xLog("原文 id: \(id)")
                if languageType == .ru {
                    
                    // 判断是否存在
                    let has = self.ruList.filter { $0.id == id }
                    
                    if has.count > 0 {
                        // 已经存在,遍历出来更新内容
                        self.ruList.forEach { model in
                            if model.id == id {
                                model.ruText = sourceText
                                model.zhText = transText
                                self.zhList.append(model)
                            }
                        }
                    } else {
                        let realtimeModel = NeoNuiResultModel(language: languageType, zhText: transText, ruText: sourceText)
                        self.ruList.append(realtimeModel)
                        self.zhList.append(realtimeModel)
                    }
                } else {
                    
                    // 判断是否存在
                    let has = self.zhList.filter { $0.id == id }
                    if has.count > 0 {
                        self.zhList.forEach { model in
                            if model.id == id {
                                model.zhText = sourceText
                                model.ruText = transText
                                self.ruList.append(model)
                            }
                        }
                    } else {
                        let realtimeModel = NeoNuiResultModel(language: languageType, zhText: sourceText, ruText: transText)
                        self.ruList.append(realtimeModel)
                        self.zhList.append(realtimeModel)
                    }
                    
                }
                self.zhTableView.reloadData()
                self.ruTableView.reloadData()
                self.scrollToBottom(tableView: self.zhTableView, list: self.zhList)
                self.scrollToBottom(tableView: self.ruTableView, list: self.ruList)
            }
        }
        
        currentTime = 0
    }
}

extension RealtimeTranslatorController: NeoNuiSdkDelegate {
    
    func onNuiEventCallback(_ nuiEvent: NuiCallbackEvent, dialog: Int, kwsResult wuw: UnsafePointer<CChar>!, asrResult asr_result: UnsafePointer<CChar>!, ifFinish finish: Bool, retCode code: Int32) {
        switch nuiEvent {
        case EVENT_TRANSCRIBER_COMPLETE: break
        case EVENT_SENTENCE_END:
            // 实时语音识别事件，检测一句话结束，返回一句的完整结果。
            let result = String(cString: asr_result)
            let dic = result.toDictionary()!
            
            if let payloadDic = dic["payload"] as? [String: Any] {
                if let res = payloadDic["result"] as? String {
                    xLog("[NeoNui] 结果 : \(res)");
                    DispatchQueue.main.async { [self] in
                        neoNuiRealtimeTranslation(res, languageType: languageType, isEnd: true)
                    }
                }
            }
            
        case EVENT_ASR_PARTIAL_RESULT:
            // 实时语音识别事件，检测一句话结束，返回一句的完整结果。
            let result = String(cString: asr_result)
            let dic = result.toDictionary()!
            
            if let payloadDic = dic["payload"] as? [String: Any] {
                if let res = payloadDic["result"] as? String {
                    xLog("[NeoNui] 中间结果 : \(res)");
                    DispatchQueue.main.async { [self] in
                        neoNuiRealtimeTranslation(res, languageType: languageType, isEnd: false)
                    }
                }
            }
            
        case EVENT_ASR_ERROR:
            xLog("[NeoNui] 错误 code:\(code)")
            // asr_result在EVENT_ASR_ERROR中为错误信息，搭配错误码code和其中的task_id更易排查问题，请用户进行记录保存。
            let ruStr = String(cString: ruNeoNui.nui_get_all_response())
            xLog("[NeoNui] ru all_response: \(ruStr)")
            
            let zhStr = String(cString: zhNeoNui.nui_get_all_response())
            xLog("[NeoNui] zh all_response: \(zhStr)")
            
            // 52010001 内部服务错误，需要客户端进行重试。
            // 240069 内部服务错误，需要客户端进行重试。
            if code == 240069 || code == 52010001 {
                xLog("[NeoNui] code:\(code) socket已关闭。")
#if DEBUG
                DispatchQueue.main.async {
                    YLHUD.showMessage("code:\(code) socket已关闭。")
                }
#endif
                DispatchQueue.global().async { [weak self] in
                    /// 异步线程
                    if self?.state == .ru {
                        // 切换语音
                        self?.startNeoNui(.ru)
                        
                    } else if self?.state == .zh {
                        // 切换语音
                        self?.startNeoNui(.zh)
                    }
                }
            }
            
        default:
            break
        }
        
        // finish 为真（可能是发生错误，也可能是完成识别）表示一次任务生命周期结束，可以开始新的识别
        if finish {
            xLog("[NeoNui] 表示一次任务生命周期结束 code:\(code)")
        }
    }
    
    
    func onNuiNeedAudioData(_ audioData: UnsafeMutablePointer<CChar>!, length len: Int32) -> Int32 {
        autoreleasepool {
            objc_sync_enter(recordedVoiceDataLock)
            defer { objc_sync_exit(recordedVoiceDataLock) }
            // xLog("onNuiNeedAudioData \(recordedVoiceData.count)")
            guard !recordedVoiceData.isEmpty else {
                emptyCount += 1
                if emptyCount >= 50 {
                    print("_recordedVoiceData length = \(recordedVoiceData.count)! empty 50 times.")
                    emptyCount = 0
                }
                return 0
            }
            
            let recorderLen = min(recordedVoiceData.count, Int(len))
            let range = 0..<recorderLen
            
            // 复制数据到 audioData
            recordedVoiceData.copyBytes(
                to: UnsafeMutableRawBufferPointer(start: audioData, count: recorderLen),
                from: range
            )
            
            // 移除已处理的数据
            recordedVoiceData.removeSubrange(range)
            emptyCount = 0
            
            return Int32(recorderLen)
        }
    }
    
    // 根据音频状态进行录音功能的开关。
    func onNuiAudioStateChanged(_ state: NuiAudioState) {
        switch state {
        case STATE_OPEN:
            recordedVoiceData = Data()
            xLog("[NeoNui] onNuiAudioStateChanged STATE_OPEN \(state)")
            
        case STATE_PAUSE:
            xLog("[NeoNui] onNuiAudioStateChanged STATE_PAUSE \(state)")
            
        case STATE_CLOSE:
            xLog("[NeoNui] onNuiAudioStateChanged STATE_CLOSE \(state)")
            
        default: break
        }
    }
    
    
    func onNuiRmsChanged(_ rms: Float) {
        // 音频能量事件。
        // xLog("[NeoNui] onNuiRmsChanged rms=\(rms)")
        let currentAmplitude = pow(10, rms / 100)
        DispatchQueue.main.async { [weak self] in
            self?.waveView.amplitude = Double(currentAmplitude)
        }
    }
    
}

// MARK: - 音频采集
extension RealtimeTranslatorController: ZegoAudioDataHandler {
    
    // 开始音频采集
    func startAudioDataObserver() {
        // 需要的音频数据类型 Bitmask，此处示例四个回调都开启
        let bitmask: ZegoAudioDataCallbackBitMask = [.captured, .player]
        let frameParam = ZegoAudioFrameParam()
        frameParam.channel = .mono
        frameParam.sampleRate = .rate16K
        // 开启获取原始音频数据功能
        ZegoExpressEngine.shared().startAudioDataObserver(bitmask, param: frameParam)
        ZegoExpressEngine.shared().setAudioDataHandler(self)
        // 开/关自动增益控制
        ZegoExpressEngine.shared().enableAGC(true)
        // 是否开启回声消除。
        ZegoExpressEngine.shared().enableAEC(true)
        // 开/关噪声抑制
        ZegoExpressEngine.shared().enableANS(true)
        // 开/关瞬态噪声抑制
        ZegoExpressEngine.shared().enableTransientANS(true)
        
    }
    
    // 停止音频采集
    func stopAudioDataObserver() {
        xLog("[NeoNui] 停止音频采集")
        
        // 异步停止，避免阻塞调用线程
        DispatchQueue.global(qos: .userInitiated).async {
            xLog("[NeoNui] 开始异步停止音频采集")
            
            // 在后台线程执行耗时的清理操作
            self.ruNeoNui.nui_dialog_cancel(true)
            self.ruNeoNui.nui_release()
            
            self.zhNeoNui.nui_dialog_cancel(true)
            self.zhNeoNui.nui_release()
            
            ZegoExpressEngine.shared().stopAudioDataObserver()
            
            xLog("[NeoNui] 异步停止音频采集完成")
        }
    }
    
    // MARK: ZegoAudioDataHandler
    func onCapturedAudioData(_ data: UnsafePointer<UInt8>, dataLength: UInt32, param: ZegoAudioFrameParam) {
        // 本地采集音频数据，推流后可收到回调
        // let sendData = Data.init(bytes: data, count: Int(dataLength))
        objc_sync_enter(recordedVoiceDataLock)
        recordedVoiceData.append(data, count: Int(dataLength))
        objc_sync_exit(recordedVoiceDataLock)
    }
    
}
